package cn.dduan.fool.design.pattern.great.proto;

import java.util.ArrayList;

/**
 * 原型模式
 * <p>
 * 核心：
 * 1、实现 Cloneable 接口
 * 2、重写 Object 类中的 clone() 方法
 * <p>
 * 优点：
 * 1、原型模式比 new方式创建对象的性能要好的多，因为Object类的 clone()方法是一个本地方法，直接操作内存中的二进制流，特别是复制大对象时，性能的差别非常明显
 * <p>
 * 适用场景：
 * 1、适合在重复地创建相似对象的场景使用（例如：循环体内创建对象，对象创建过程比较复杂或者循环次数很多的话，使用原型模式不但可以简化创建过程，而且也可以提供系统的整体性能）
 * <p>
 * 注意：
 * 1、原型模式复制对象不会调用类的构造函数，单例模式与原型模式是冲突的，在使用时需要注意
 * 2、深拷贝（对象所引用的对象都克隆了一遍）与浅拷贝（基本数据类型）
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/7
 */
public class Prototype {
    public static void main(String[] args) {
        ShallowClone cp = new ShallowClone();
        ShallowClone clonecp = (ShallowClone) cp.clone();
        clonecp.show();
        System.out.println(clonecp.list == cp.list);

        DeepClone dc = new DeepClone();
        DeepClone clonecp2 = (DeepClone) dc.clone();
        clonecp2.show();
        System.out.println(clonecp2.list == dc.list);
    }
}

/**
 * 实现原型模式
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/7
 */
abstract class Proto implements Cloneable {
    protected ArrayList<String> list = new ArrayList<String>();

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public abstract void show();
}

/**
 * 浅拷贝
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/7
 */
class ShallowClone extends Proto {
    @Override
    public Proto clone() {
        Proto prototype = null;
        try {
            prototype = (Proto) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }

    @Override
    public void show() {
        System.out.println("浅克隆");
    }
}

/**
 * 深拷贝
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/7
 */
class DeepClone extends Proto {
    @SuppressWarnings("unchecked")
    @Override
    public Proto clone() {
        Proto prototype = null;
        try {
            prototype = (Proto) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        prototype.list = (ArrayList<String>) this.list.clone();
        return prototype;
    }

    @Override
    public void show() {
        System.out.println("深克隆");
    }
}